import {mat4, quat, vec3, vec4} from 'gl-matrix';
import {renderTriangle} from './renderTriangle';


type v2 = [number, number];
type v3 = [number, number, number];
type v4 = [number, number, number, number];


function transform(point: [number, number, number], mat: mat4) {
  let next = vec3.create();
  vec3.transformMat4(next, point, mat);
  const [x, y, z] = next as number[];
  return {x, y, z};
}

function toMatModel(scale: [number, number, number], rotate: [number, number, number], translate: [number, number, number]) {
  // M - 缩放
  function doScale(matMain: mat4) {
    mat4.scale(matMain, matMain, scale);
  }
  
  // M - 旋转
  function matRotate(matMain: mat4) {
    let _q = quat.create();
    let _m = mat4.create();
    quat.fromEuler(_q, rotate[0], rotate[1], rotate[2]);
    mat4.fromQuat(_m, _q);
    mat4.mul(matMain, matMain, _m);
  }
  
  // M - 移动
  function matMove(matMain: mat4) {
    mat4.translate(matMain, matMain, translate);
  }
  
  function getMatModel() {
    let mat = mat4.create();
    matMove(mat);
    matRotate(mat);
    doScale(mat);
    return mat;
  }
  
  let matModel = getMatModel();
  return {matScale: doScale, matRotate, matMove, matModel};
}

export async function draw(
  context: CanvasRenderingContext2D,
  scale: v3,
  rotate: v3,
  translate: v3,
  eyePos: v3,
  eyeCenter: v3,
  eyeTop: v3,
  fovY: number = Math.PI / 3 * 2,
  near: number = 1,
  far: number = 100,
  texture: ImageData,
) {
  let {width, height} = context.canvas;
 
  let matTarget = mat4.create();
  let {matScale, matRotate, matMove, matModel} = toMatModel(scale, rotate, translate);
  
  console.log(matModel)
  
  // 相机空间 V
  function matLookAt(matMain: mat4) {
    
    
    let _l = mat4.create();
    mat4.lookAt(
      _l,
      eyePos,
      eyeCenter,
      eyeTop,
    );
    mat4.mul(matMain, matMain, _l);
    
    
  }
  
  // 投影 P
  function matProj(matMain: mat4) {
    let _p = mat4.create();
    mat4.perspective(
      _p,
      fovY,
      1, //width / height,
      near,
      far,
    );
    mat4.mul(matMain, matMain, _p);
    
    
  }
  
  let mute = (cb: any) => {
    let log = console.log;
    console.log = () => {
    };
    cb();
    console.log = log;
  };
  
  function printMainMat() {
  
  
  }
  
  matProj(matTarget);
  matLookAt(matTarget);
  matMove(matTarget);
  matRotate(matTarget);
  matScale(matTarget);
  printMainMat();
  
  
  let shape: any = null;
  const {
    triangles,
    points,
    colors,
    uv,
  } = shape;
  let zBuffer = new Array(width * height).fill(999999);
  let buffer = new ImageData(width, height);
  
  for (let triangle of triangles) {
    let _points = triangle.map(x => points[x]);
    let _colors: [vec4, vec4, vec4] = triangle.map(x => colors[x]) as [vec4, vec4, vec4];
    
    function computeNormal() {
      let normal: any = null;
      mute(() => {
        
        let line1 = vec3.create();
        let line2 = vec3.create();
        let matN = mat4.create();
        matMove(matN);
        matRotate(matN);
        matScale(matN);
        const [p1, p2, p3] = _points.map(point => {
          let target = vec3.create();
          vec3.transformMat4(target, point, matN);
          return target;
        });
        vec3.subtract(line1, p1, p2);
        vec3.subtract(line2, p2, p3);
        normal = vec3.create();
        
        vec3.cross(normal, line2, line1);
        vec3.normalize(normal, normal);
      });
      return normal;
    }
    
    let normal = computeNormal();
    
    let uvs: [v2, v2, v2] | undefined = uv ? triangle.map(x => uv[x]) as [v2, v2, v2] : undefined;
    let xys = _points.map(point => {
      const {x, y, z} = transform(point, matTarget);
      //
      //   'point',
      //   ,
      //   ' => ',
      //   ,
      // );
      return [x, y, z];
    });
    
    
    await renderTriangle(
      buffer,
      xys,
      _colors,
      context,
      zBuffer,
      normal,
      uvs,
      texture,
    );
  }
  
  async function renderPointO() {
    let points: v3[] = [
      [1, 0, 0],
      [0, 1, 0],
      [0, 0, 1],
    ];
    let mat = mat4.create();
    mute(() => {
      matProj(mat);
      matLookAt(mat);
    });
    points = points.map(point => {
      let {x, y, z} = transform(point, mat);
      return [x, y, z];
    });
    
    
    await renderTriangle(
      buffer,
      points,
      [
        [1, 0, 0, 1],
        [0, 1, 0, 1],
        [0, 0, 1, 1],
      ],
      context,
      zBuffer,
      [0, 0, 1],
    );
  }
  
  await renderPointO();
  
  
  context.putImageData(buffer, 0, 0);
}
